﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.Threading;
using System.Reflection;
using System.ComponentModel;

using ChangeNotifierSelector = System.Func<System.Reflection.MemberInfo, ReactiveLang.State.ChangeNotifier>;

namespace ReactiveLang.State
{
    using Util;

    public class ChangeNotifierOptions
    {
        public IExpression<TimeSpan> Interval { get; set; }
    }
    public delegate IExpression<Unit> ChangeNotifier(IList<object> arguments, ChangeNotifierOptions options);

    public static partial class ChangeNotifications
    {
        static LinkedList<ChangeNotifierSelector> _selectors = new LinkedList<ChangeNotifierSelector>();
        static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

        public static IDisposable RegisterNotifier(ChangeNotifierSelector selector)
        {
            Contract.Requires(selector != null);

            using (_lock.WriteLock())
            {
                var node = _selectors.AddLast(selector);
                return Disposable.Create(delegate
                {
                    using (_lock.WriteLock())
                    {
                        node.List.Remove(node);
                    }
                });
            }
        }
        internal static IDisposable RegisterNotifier<TMember>(Func<TMember, ChangeNotifier> selector)
            where TMember : MemberInfo
        {
            return RegisterNotifier(m =>
            {
                var member = m as TMember;
                return member != null ? selector(member) : null;
            });
        }

        public static ChangeNotifier TryGetNotifier(MemberInfo member)
        {
            Contract.Requires<ArgumentNullException>(member != null, "member");

            member = member.Normalize();

            using (_lock.ReadLock())
            {
                for (var n = _selectors.Last; n != null; n = n.Previous)
                {
                    var observer = n.Value(member);
                    if (observer != null)
                        return observer;
                }
            }

            return null;
        }
        public static ChangeNotifier GetNotifier(MemberInfo member)
        {
            var notifier = TryGetNotifier(member);
            if (notifier == null)
                notifier = delegate { return Expression.Unit; };
            return notifier;
        }

    }
}